// OpenZapper
// http://openzapper.codeplex.com/
// Appache License Version 2.0 


using System;
using Microsoft.SPOT;
using MicroLiquidCrystal;
using SecretLabs.NETMF.Hardware.Netduino;
using Microsoft.SPOT.Hardware;
using System.Threading;

namespace OpenZapper
{
    public class Zapper:IDisposable, IZapperCallback
    {
        GpioLcdTransferProvider lcdProvider;
        Lcd lcd;
        ConfigReader Config;
        bool bhasStopped = false;
        PWM _Zapper;
        ZapperCycleThread zapperThread = null;

        bool StartupMode = true;
        bool bSdCardFound = false;
        int m_iCurrentProfileIdx = 0;

        InterruptPort m_iSwitchUp;
        InterruptPort m_iSwitchDown;
        InterruptPort m_iSwitchOk;
        InterruptPort m_iSwitchCancel;
        
        OutputPort ledStandby = new OutputPort(Pins.GPIO_PIN_D10, false);
        OutputPort ledRunning = new OutputPort(Pins.GPIO_PIN_D13, false);
        DateTime Button_LastInterruptTime = DateTime.Now;

        object ButtonLock = new object();

        public Zapper()
        {

            _Zapper = new PWM(Cpu.PWMChannel.PWM_1, 2500, 1, false);

            Config = new ConfigReader("zapper.ini");
            lcdProvider  = new GpioLcdTransferProvider(rs: Pins.GPIO_PIN_D12,
                                                        enable: Pins.GPIO_PIN_D11,
                                                        d4: Pins.GPIO_PIN_D2,
                                                        d5: Pins.GPIO_PIN_D3,
                                                        d6: Pins.GPIO_PIN_D4,
                                                        d7: Pins.GPIO_PIN_D5);
             lcd = new Lcd((ILcdTransferProvider)lcdProvider);

             EnableInterrupts();
            
        }
        private void EnableInterrupts()
        {
            m_iSwitchUp = new InterruptPort(Pins.GPIO_PIN_D8,
                                            true,//true
                                            Port.ResistorMode.PullUp,
                                            Port.InterruptMode.InterruptEdgeLow);
            m_iSwitchDown = new InterruptPort(Pins.GPIO_PIN_D7,
                                               true,//true
                                               Port.ResistorMode.PullUp,
                                               Port.InterruptMode.InterruptEdgeLow);
            m_iSwitchOk = new InterruptPort(Pins.GPIO_PIN_D0,
                                           true,//true
                                           Port.ResistorMode.PullUp,
                                           Port.InterruptMode.InterruptEdgeLow);
            m_iSwitchCancel = new InterruptPort(Pins.GPIO_PIN_D1,
                                                true,//true
                                                Port.ResistorMode.PullUp,
                                                Port.InterruptMode.InterruptEdgeLow);
            try
            {
                m_iSwitchUp.OnInterrupt     += new NativeEventHandler(this.OnUp);
                m_iSwitchDown.OnInterrupt   += new NativeEventHandler(this.OnDown);
                m_iSwitchOk.OnInterrupt     += new NativeEventHandler(this.OnOk);
                m_iSwitchCancel.OnInterrupt += new NativeEventHandler(this.OnCancel);
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message);
            }

            try
            {
                m_iSwitchUp.EnableInterrupt();
                m_iSwitchDown.EnableInterrupt();
                m_iSwitchOk.EnableInterrupt();
                m_iSwitchCancel.EnableInterrupt();
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message);
            }
        }
        private void DisableInterrupts()
        {
            m_iSwitchUp.DisableInterrupt();
            m_iSwitchDown.DisableInterrupt();
            m_iSwitchOk.DisableInterrupt();
            m_iSwitchCancel.DisableInterrupt();

            m_iSwitchUp.OnInterrupt     -= new NativeEventHandler(this.OnUp);
            m_iSwitchDown.OnInterrupt   -= new NativeEventHandler(this.OnDown);
            m_iSwitchOk.OnInterrupt     -= new NativeEventHandler(this.OnOk);
            m_iSwitchCancel.OnInterrupt -= new NativeEventHandler(this.OnCancel);

            m_iSwitchUp.Dispose();
            m_iSwitchDown.Dispose();
            m_iSwitchOk.Dispose();
            m_iSwitchCancel.Dispose();

            m_iSwitchUp     = null;
            m_iSwitchDown   = null;
            m_iSwitchOk     = null;
            m_iSwitchCancel = null;
        }

        public void Start()
        {
            lcd.Begin(columns: 16, lines: 2);
            lcd.Clear();
            lcd.WriteLine(0, "Starting...");

            _Zapper.Start();
            ledStandby.Write(false);
            ledRunning.Write(false);
            Thread.Sleep(100);
            

            Config.LoadSettings();
            if (false == Config.LoadProfiles())
            {
                lcd.WriteLine(0, "No SDCard Found");
                lcd.WriteLine(1, "Press Any Key");
            }
            else
            {
                lcd.WriteLine(0, "SDCard Found");
                lcd.WriteLine(1, "Press Any Key");
                bSdCardFound = true;
            }
            while (true)
            {
                Thread.Sleep(1000);
            }
            StopZapper();
            _Zapper.Stop();
            _Zapper.Dispose();
        }

        
        private void StopZapper()
        {
            _Zapper.DutyCycle = 100;//inverted dutyCycle due to transistor invertion
            ledStandby.Write(true);
            ledRunning.Write(false);
        }

        public void Stop()
        {
            bhasStopped = true;
        }

        public void Dispose()
        {
            if (false == bhasStopped)
            {
                Stop();
            }
        }

        public void DisplayProfiles(bool SdCardFound, int idx)
        {
            lcd.Clear();
            //if (SdCardFound == false)
            {
                int iProfilesCount = Config.getProfilesSize();
                if (idx < iProfilesCount)
                {
                    lcd.WriteLine(0, "*" + (Config.getProfileByIdx(idx)).ProfileName);
                    if (idx + 1 < iProfilesCount)
                    {
                        lcd.WriteLine(1, " " + (Config.getProfileByIdx(idx + 1)).ProfileName);
                    }
                    else
                    {
                        lcd.WriteLine(1, " " + (Config.getProfileByIdx(0)).ProfileName);
                    }
                }
                else
                {
                    lcd.WriteLine(0, "*" + (Config.getProfileByIdx(0)).ProfileName);
                    if (1 < iProfilesCount)
                    {
                        lcd.WriteLine(1, " " + (Config.getProfileByIdx(1)).ProfileName);
                    }
                    else
                    {
                        lcd.WriteLine(1, " " + (Config.getProfileByIdx(0)).ProfileName);
                    }
                }
            }
        }
        private void OnDown(uint port, uint state, DateTime time)
        {
            if (StartupMode == false)
            {
                if (Button_LastInterruptTime.AddMilliseconds(150) < time)
                {
                    Button_LastInterruptTime = time;
                    if (StartupMode == false)
                    {
                        m_iCurrentProfileIdx += 1;
                        if (m_iCurrentProfileIdx >= Config.getProfilesSize())
                        {
                            m_iCurrentProfileIdx = 0;
                        }
                        DisplayProfiles(bSdCardFound, m_iCurrentProfileIdx);
                        Thread.Sleep(100);
                    }
                }
            }
            else
            {
                m_iCurrentProfileIdx = 0;
                DisplayProfiles(bSdCardFound, m_iCurrentProfileIdx);
                StartupMode = false;
                ZapperStandby();
            }
        }
        private void OnUp(uint port, uint state, DateTime time)
        {
            if (StartupMode == false)
            {
                if (Button_LastInterruptTime.AddMilliseconds(150) < time)
                {
                    Button_LastInterruptTime = time;
                    if (StartupMode == false)
                    {
                        m_iCurrentProfileIdx -= 1;
                        if (m_iCurrentProfileIdx < 0)
                        {
                            m_iCurrentProfileIdx = Config.getProfilesSize() - 1;
                        }
                        DisplayProfiles(bSdCardFound, m_iCurrentProfileIdx);
                    }
                }
            }
            else
            {
                m_iCurrentProfileIdx = 0;
                DisplayProfiles(bSdCardFound, m_iCurrentProfileIdx);
                StartupMode = false;
                ZapperStandby();
            }
        }
        private void OnOk(uint port, uint state, DateTime time)
        {
            if (StartupMode == false)
            {
                lock (ButtonLock)
                {
                    if (zapperThread == null)
                    {
                        zapperThread = new ZapperCycleThread(_Zapper, Config, lcd, this);
                        zapperThread.Start(Config.getProfileByIdx(m_iCurrentProfileIdx));
                    }
                }
            }
            else
            {
                m_iCurrentProfileIdx = 0;
                DisplayProfiles(bSdCardFound, m_iCurrentProfileIdx);
                StartupMode = false;
                ZapperStandby();
            }
        }
        private void OnCancel(uint port, uint state, DateTime time)
        {
            if (StartupMode == false)
            {
                lock (ButtonLock)
                {
                    if (zapperThread != null)
                    {
                        zapperThread.Stop();
                        Thread.Sleep(2000);
                        zapperThread.Dispose();
                        DisplayProfiles(bSdCardFound, m_iCurrentProfileIdx);
                        zapperThread = null;
                        /*DisableInterrupts();
                        EnableInterrupts();*/
                    }
                }
            }
            else
            {
                m_iCurrentProfileIdx = 0;
                DisplayProfiles(bSdCardFound, m_iCurrentProfileIdx);
                StartupMode = false;
                ZapperStandby();
            }
        }

        /*private void OnInterrupt(uint port, uint state, DateTime time)
        {
            if (Button_LastInterruptTime.AddMilliseconds(150) < time)
            {
                Button_LastInterruptTime = time;
                if (StartupMode == false)
                {
                    switch (port)
                    {
                        case 1://down
                            m_iCurrentProfileIdx += 1;
                            if (m_iCurrentProfileIdx >= Config.getProfilesSize())
                            {
                                m_iCurrentProfileIdx = 0;
                            }
                            DisplayProfiles(bSdCardFound, m_iCurrentProfileIdx);
                            Thread.Sleep(100);
                            break;
                        case 0://up
                            m_iCurrentProfileIdx -= 1;
                            if (m_iCurrentProfileIdx < 0)
                            {
                                m_iCurrentProfileIdx = Config.getProfilesSize() - 1;
                            }
                            DisplayProfiles(bSdCardFound, m_iCurrentProfileIdx);
                            
                            break;
                        case 39://OK
                        case 7://OK
                            lock (ButtonLock)
                            {
                                if (zapperThread == null)
                                {
                                    zapperThread = new ZapperCycleThread(_Zapper, Config, lcd, this);
                                    zapperThread.Start(Config.getProfileByIdx(m_iCurrentProfileIdx));
                                }
                            }
                            break;
                        case 38://cancel
                        case 6://cancel
                            lock (ButtonLock)
                            {
                                if (zapperThread != null)
                                {
                                    zapperThread.Stop();
                                    Thread.Sleep(2000);
                                    zapperThread.Dispose();
                                    DisplayProfiles(bSdCardFound, m_iCurrentProfileIdx);
                                    zapperThread = null;
                                    //DisableInterrupts();
                                    //EnableInterrupts();
                                }
                            }
                            break;
                        default:
                            break;
                    }
                }
                else
                {
                    m_iCurrentProfileIdx = 0;
                    DisplayProfiles(bSdCardFound, m_iCurrentProfileIdx);
                    StartupMode = false;
                    ZapperStandby();
                }
            }
        }*/

        public void ZapperStandby()
        {
            ledStandby.Write(true);
            ledRunning.Write(false);
        }

        public void ZapperRunning()
        {
            ledStandby.Write(false);
            ledRunning.Write(true);
        }

        public void ZapperPauseEnded()
        {
            for (int i = 0; i < 10; i++)
            {
                ledStandby.Write(true);
                ledRunning.Write(true);
                Thread.Sleep(1000);
                ledStandby.Write(false);
                ledRunning.Write(false);
                Thread.Sleep(1000);
            }
            ZapperStandby();
        }
    }
}
